概述
属性动画是在Android3.0提供的一套全新的动画Api,它和传统的补间动画相比有更大的灵活性。比如我们要使用动画更新一个button的宽度,如果使用补间动画ScaleAnimation那么最后放大后的button是变形的,而使用属性动画实现不存在这样的问题,还有最大的区别在于补间动画不会真正的改变view的属性,而属性动画会。
本篇将从源码角度对属性动画进行分析
ValueAnimation
关于属性动画,我们最常用的类便是ValueAnimation和ObjectAnimator,其中ValueAnimation是ObjectAnimator的父类。它是属性动画实现的核心,所以我们先来看看ValueAnimation的实现
####继承关系
1 | public class ValueAnimator extends Animator { |
ValueAnimator继承自Animator,Animator是属性动画最基本的类,它是一个抽象类,定义了一些属性动画的接口,如start,cancel,setduration,addListener等等,ValueAnimator一般是通过使用其静态的of方法来构造,这里我们看看ofInt的实现,其他of方法实现类似
1 | public static ValueAnimator ofInt(int... values) { |
ofInt构造ValueAnimator实例并通过setIntValues为其设置初始值。
1 | public void setIntValues(int... values) { |
mValues是一个PropertyValuesHolder数组,从名称来看PropertyValuesHolder是属性和值的持有者,它维护属性和值的相关信息。开始mValues是null,继续调用setValues,同时使用PropertyValuesHolder的ofInt方法构造PropertyValuesHolder实例作为参数,属性名为空。
1 | public static PropertyValuesHolder ofInt(String propertyName, int... values) { |
mValuesMap是一个Map,key值时属性名,value是PropertyValuesHolder,对于每一个属性的PropertyValuesHolder都会保存在mValuesMap中。
1 |
|
ValueAnimation的启动默认调用了内部的private的start方法,参数playBackwards为false,表示动画是否反向执行。随后通过getOrCreateAnimationHandler创建一个AnimationHandler,然后将当前动画添加到其内部的等待队列mPendingAnimations中,这个animationHandler成员是个AnimationHandler,它是ValueAnimation内部类,如果未设置mStartDelay,则开始执行动画第一帧,随后通过notifyStartListeners通知动画Listener动画启动。
1 | public void setCurrentPlayTime(long playTime) { |
setCurrentPlayTime是执行playTime指定的时间点的动画,这个playTime是在[0,duration]的区间内。在setCurrentPlayTime需要先初始化动画,然后设置mSeekTime为0,执行状态为SEEKED,随后通过doAnimationFrame执行动画。
1 | void initAnimation() { |
动画只需要初始化一次,主要是对PropertyValuesHolder进行初始化,后面我们在看关于PropertyValuesHolder的初始化部分。初始化通过mInitialized标记控制。
1 | final boolean doAnimationFrame(long frameTime) { |
mSeekTime为0,mStartTime为frameTime值,即当前的时间值。如果mPaused表示暂停动画中,返回false,mResumed为true表示动画继续执行,此时根据mPauseTime重新计算mStartTime,即动画的开始执行时间,随后取frameTime和mStartTime的最大值传递给animationFrame进一步执行动画。
1 | boolean animationFrame(long currentTime) { |
animationFrame中首先根据mDuration计算fraction,fraction即时间流逝的百分比,它的值是客观均匀的,随后通过animateValue计算动画值,这个方法返回动画是否执行完毕。
1 | void animateValue(float fraction) { |
通过fraction和插值器计算动画插值,我们知道插值器是能够控制动画的执行速率,它根据fraction值计算,ValueAnimation中默认的插值器是AccelerateDecelerateInterpolator,随后通过PropertyValuesHolder的calculateValue根据插值计算动画值,这个动画值就是我们在of方法指定的区间内的值,最后通过onAnimatinoUpdate来通知动画值的更新。到这里我们的动画第一帧就算执行了,那么它如何连续执行呢?这就要靠AnimationHandler了。
1 | protected static ThreadLocal<AnimationHandler> sAnimationHandler = |
其中AnimationHandler的说明如下
1 | /** |
可以看出AnimationHandler在线程内是由所有活动的动画共享的。它可以保证animation动画值设置操作发生在UI线程,而所有的动画将共享相同的相同值来计算它们自己的值以使动画同步执行。这个handler使用Choreographer来执行周期性的回调。这个Choreographer可以看做是一个Vsync信号的观察者,它为上层应用提供监听VSync信号的接口,应用程序根据该信号刷新UI。
在start的方法最后调用了AnimationHandler的start,我们看看它的内部如何实现
1 |
|
Animationhandler是通过Choreographer来执行动画的,在start方法中调用scheduleAnimation添加当前AnimationHandler到Choreographer的回调队列中,它的类型为Choreographer.CALLBACK_ANIMATION,Choreographer在接收到VSync信号时会通知该回调也就是调用回调的run方法,这里就是执行doAnimationFrame方法,同时重置mAnimationScheduled。所以真正执行动画的是通过doAnimationFrame来完成的。
1 | private void doAnimationFrame(long frameTime) { |
在doAnimationFrame中首先通过startAnimation将等待的动画启动,它其实将动画添加到活动的动画列表mAnimations并调用开始执行动画的回调。随后对于活动列表中的动画执行doAnimationFrame,它执行一帧动画。对于执行完成的动画调用endAnimation,它分别从活动列表,等待列表,延时列表中移除该动画实例。最后只要有活动的动画,就需要调用scheduleAnimation安排下一帧动画的执行。这样动画就能够连续执行了。
PropertyValuesHolder
在ValueAnimation动画的执行过程中依赖于PropertyValuesHolder,只不过这时候的PropertyName为空,在整个流程中涉及到PropertyValuesHolder的有下面几个部分:
- 在setValues中通过of方法实例化PropertyValuesHolder
- 在动画初始化initAnimation方法中调用其init方法
- 在animateValue中通过PropertyValuesHolder的calculateValue计算动画值
下面就分别看看这些具体是如何实现的
设置values
1 | public static PropertyValuesHolder ofInt(String propertyName, int... values) { |
1 | static class IntPropertyValuesHolder extends PropertyValuesHolder { |
1 | //PropertyValuesHolder.java |
我们设置的values最终是以KeyFrameSet的形式存在的,KeyframeSet实际上就是关键帧的值,包含了我们设置的值,以及
每个值对应的fraction。
初始化
1 | void init() { |
init实际上是为PropertyValuesHolder设置估值器,sIntEvaluator实际上就是整型的估值器,在计算动画值时使用,它的实现如下
1 | public class IntEvaluator implements TypeEvaluator<Integer> { |
计算动画值
1 | void calculateValue(float fraction) { |
动画值最终是通过KeyFrameSet来计算的,我们通过of方法构造的KeyFrameSet,这里通过fraction来计算最终的动画值,fraction可能是经过插值器计算后的,对于关键帧数为2的最简单,直接通过估值器计算即可,startValue就是第一个关键帧的值,endValue就是最后一个关键帧。而当关键帧数目大于2的时候,需要这么计算,这里我举个例子比如ofInt(1,100,200),关键帧的数目为3,对应的KeyFrame分别为[0f,1],[0.5f,100],[1f,200]这里的0f,0.5f,1f分别是keyFrame对应的fraction。在getValue中根据fraction计算动画值时:
- 如果此时fraction小于0.5,则根据估值器进行如下计算evaluate(intervalFraction,0,100);
- 如果fraction大于0.5则进行如下计算evaluate(intervalFraction,100,200)
ObjectAnimation
ObjectAnimator是ValueAnimation的子类,它实际上同ValueAnimation的区别在于,ObjectAnimation包含一个Target和PropertyName,可以在该Target上的Property上应用动画,这个Target不仅仅可以是View。只要Target在该PropertyName上具有setter和getter方法即可。在ObjectAnimation中重载了initAnimation和animateValue方法
1 |
|
ObjectAnimation相比ValueAnimation会调用PropertyValuesHolder的setupSetterAndGetter方法,从名称上看应该是建立Target的对应属性的setter和getter方法。
1 | void setupSetterAndGetter(Object target) { |
setupSetterOrGetter方法会通过getPropertyFunction获取属性对应的get或者set方法,获取到以后缓存到propertyMapMap中,propertyMapMap以target实例为key,以一个Map<String,Method>为value。这个Map是用于保存属性对应的Method,其中key就是属性的名称。
1 | //ObjectAnimator.java |
ObjectAnimator的animateValue和ValueAnmiation的实现是不同的,ObjectAnimator会通过PropertyValuesHolder的setAnimatedValue方法为Target设置属性值。